package level.editor.control.main;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.TreeMap;

import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import level.editor.control.Handler;
import level.editor.control.collision.CollisionLayer;
import level.editor.control.layer.Layer;
import level.editor.control.layer.LayerBuffer;
import level.editor.control.layer.LayerImage;
import level.editor.control.object.ObjectLayer;
import level.editor.control.tile.TileConstants;
import level.editor.control.tile.TileLayer;
import level.editor.model.main.MainModel;
import level.model.Addable;
import level.model.Selection;
import level.model.State;

public class Main extends JLayeredPane
{
	public final static Boolean DIRTY = new Boolean(true);
	public final static Boolean NOT_DIRTY = new Boolean(false);
	public final static String DIRTY_PROPERTY = "Dirty";

	public Main(State state)
	{
		super();
		model = new MainModel(state, this);
	}

	public void init(Dimension size)
	{
		setPreferredSize(new Dimension((int) (size.getWidth() * 2),
				(int) (size.getHeight() * 2)));
		setBounds(new Rectangle((int) -size.getWidth() / 2,
				(int) -size.getHeight() / 2, (int) getPreferredSize()
						.getWidth(), (int) getPreferredSize().getHeight()));
		origin = new Point((int) size.getWidth(), (int) size.getHeight());

		addLayer(new BackgroundPanel(), BACKGROUND_LAYER);
		addLayer(minimap = new Minimap(), MINIMAP_LAYER);
		addLayer(new CurrentTool(this), CURRENT_TOOL_LAYER);

		// Initiate state here
		setDirty(NOT_DIRTY);
		model.initiateState();
		handler = null;

		DragScrollHandler.createDragScrollHandlerFor(this);
	}

	public void setDirty(Boolean dirty)
	{
		putClientProperty(DIRTY_PROPERTY, dirty ? DIRTY : NOT_DIRTY);
		if (getMainTabbed() != null)
		{
			MainTabbed tabbed = getMainTabbed();
			tabbed.setTitleAt(tabbed.getSelectedIndex(), getName());
			tabbed.getTabComponentAt(tabbed.getSelectedIndex()).revalidate();
		}
	}

	public void createMemento()
	{
		model.createMemento();
	}

	public Boolean isDirty()
	{
		if (getClientProperty(DIRTY_PROPERTY) != null)
		{
			return (Boolean) getClientProperty(DIRTY_PROPERTY);
		}
		else
			return null;
	}

	public Handler getHandler()
	{
		return handler;
	}

	public void updateMinimap(int layerNumber, LayerImage image)
	{
		minimap.put(new Integer(layerNumber), image);
	}

	public void minimapUpdate()
	{
		for (int i = 0; i <= MAX_STATE_LAYERS; i++)
		{
			for (Component layer : getComponentsInLayer(new Integer(i)))
			{
				layer.setSize(getSize());
				if (layer instanceof TileLayer)
					((TileLayer) layer).requestMinimap();
			}
		}
	}

	public void paintFromState()
	{
		for (int i = 0; i <= MAX_STATE_LAYERS; i++)
		{
			for (Component layer : getComponentsInLayer(new Integer(i)))
			{
				if (layer instanceof Layer)
				{
					layer.setSize(getSize());
					((Layer) layer).paintFromState();
					// if (layer instanceof TileLayer)
					// ((TileLayer) layer).requestMinimap();
				}
			}
		}
	}

	public Point getOrigin()
	{
		return origin;
	}

	public void setOrigin(Point origin)
	{
		this.origin = origin;
	}

	public void addLayer(Component layer, Integer layerNumber)
	{
		layer.setSize(getSize());
		add(layer, layerNumber);
	}

	public String getName()
	{
		return model.getName();
	}

	public TreeMap<Integer, Layer> getLayers()
	{
		TreeMap<Integer, Layer> layers = new TreeMap<Integer, Layer>();
		for (int i = 0; i <= MAX_STATE_LAYERS; i++)
		{
			for (Component layer : getComponentsInLayer(new Integer(i)))
			{
				if (!(layer instanceof LayerBuffer))
					layers.put(i, (Layer) layer);
			}
		}
		return layers;
	}

	public Layer getSelectedLayer()
	{
		for (int i = 0; i <= MAX_TILE_LAYERS; i++)
		{
			for (Component layer : getComponentsInLayer(new Integer(i)))
			{
				if (layer instanceof TileLayer)
				{
					TileLayer layer_N = (TileLayer) layer;
					if (layer_N.isSelected())
						return layer_N;
				}
			}
		}
		return null;
	}

	public MainTabbed getMainTabbed()
	{
		if (getParent() != null)
		{
			if (getParent().getParent() != null
					&& getParent().getParent() instanceof MainScroll)
			{
				if (getParent().getParent().getParent() != null
						&& getParent().getParent().getParent() instanceof MainTabbed)
				{
					return (MainTabbed) getParent().getParent().getParent();
				}
			}
		}
		return null;
	}

	public MainScroll getScroll()
	{
		if (getParent() != null)
		{
			if (getParent().getParent() != null
					&& getParent().getParent() instanceof MainScroll)
			{
				return (MainScroll) getParent().getParent();
			}
		}
		return null;
	}

	public CollisionLayer getCollisionLayer()
	{
		for (Component layer : getComponentsInLayer(new Integer(COLLISION_LAYER)))
			if (!(layer instanceof LayerBuffer)
					&& layer instanceof CollisionLayer)
				return (CollisionLayer) layer;
		return null;
	}
	
	public ObjectLayer getObjectLayer()
	{
		for (Component layer : getComponentsInLayer(new Integer(OBJECT_LAYER)))
			if (!(layer instanceof LayerBuffer)
					&& layer instanceof ObjectLayer)
				return (ObjectLayer) layer;
		return null;
	}

	public ArrayList<Layer> getUnlockedLayers()
	{
		ArrayList<Layer> layers = new ArrayList<Layer>();
		for (int i = 0; i <= MAX_STATE_LAYERS; i++)
		{
			for (Component comp : getComponentsInLayer(new Integer(i)))
			{
				if (comp instanceof Layer)
				{
					layers.add((Layer) comp);
				}
			}
		}
		return layers;
	}

	public void save()
	{
		model.save();
	}

	public boolean saveAs()
	{
		return model.saveAs();
	}

	public boolean undo()
	{
		return model.undo();
	}

	public boolean redo()
	{
		return model.redo();
	}

	public void updateFile()
	{
		model.updateFile();
	}

	public void resetLayers()
	{
		for (int i = 0; i <= MAX_STATE_LAYERS; i++)
		{
			for (Component layer : getComponentsInLayer(new Integer(i)))
			{
				remove(layer);
			}
		}
	}

	public Selection select(Point pt)
	{
		Selection selection = new Selection();
		for (int i = 0; i <= MAX_STATE_LAYERS; i++)
		{
			for (Component comp : getComponentsInLayer(new Integer(i)))
				if (comp instanceof Layer)
				{
					ArrayList<Addable> selected = new ArrayList<Addable>();
					Layer layer = (Layer) comp;
					if (!layer.isLocked() && layer.isVisible())
					{
						
						Addable addable = layer.select(pt);
						if (addable != null)
							selected.add(addable);
					}

					if (!selected.isEmpty())
						selection.add(i, selected);
					layer.setSize(getSize());
					layer.paintFromState();
				}
		}

//		minimapUpdate();
		if (!selection.isEmpty())
			return selection;
		else
			return null;
	}
	
	public Selection select(Rectangle rect)
	{
		Selection selection = new Selection();
		
		for (int i = 0; i <= MAX_STATE_LAYERS; i++)
		{
			for (Component comp : getComponentsInLayer(new Integer(i)))
				if (comp instanceof Layer)
				{
					ArrayList<Addable> selected = null;
					Layer layer = (Layer) comp;
					if (!layer.isLocked() && layer.isVisible())
						selected = layer.select(rect);

					if (selected != null && !selected.isEmpty())
						selection.add(i, selected);
					layer.setSize(getSize());
					layer.paintFromState();
				}
		}
		
		if (!selection.isEmpty())
			return selection;
		else
			return null;
	}

	public LayerBuffer getLayerBuffer(Layer layer)
	{
		for (Component layerBuffer : getComponentsInLayer(layer
				.getLayerNumber()))
		{
			if (layerBuffer instanceof LayerBuffer)
			{
				return (LayerBuffer) layerBuffer;
			}
		}
		return null;
	}

	public void removeHandler(Handler listener)
	{
		if (listener != null)
			listener.finish();
		removeMouseListener(listener);
		removeMouseMotionListener(listener);
		removeKeyListener(listener);
		removeGhosts();
		handler = null;
	}

	public void addHandler(Handler listener)
	{
		addMouseListener(listener);
		addMouseMotionListener(listener);
		addKeyListener(listener);
		handler = listener;
	}

	private void removeGhosts()
	{
		for (int i = 0; i <= MAX_STATE_LAYERS; i++)
		{
			for (Component layer : getComponentsInLayer(new Integer(i)))
			{
				layer.setSize(getSize());
				if (layer instanceof Layer)
					((Layer) layer).setGhost(null, null);
			}
		}
	}

	private class BackgroundPanel extends JPanel
	{
		public BackgroundPanel()
		{
			super(new BorderLayout());
			setBackground(Color.BLACK);
			setVisible(true);
			setOpaque(true);
		}

		@Override
		public void paintComponent(Graphics gOld)
		{
			setSize(getParent().getSize());
			super.paintComponent(gOld);
			if (displayGrid)
			{
				Graphics2D g = (Graphics2D) gOld;
				g.setRenderingHints(new RenderingHints(
						RenderingHints.KEY_ANTIALIASING,
						RenderingHints.VALUE_ANTIALIAS_ON));
				g.setStroke(new BasicStroke(3f));
				g.setColor(Color.WHITE);

				g.draw(new Line2D.Float(0, (int) origin.getY(),
						(float) getSize().getWidth(), (int) origin.getY()));

				g.draw(new Line2D.Float((int) origin.getX(), 0, (int) origin
						.getX(), (float) getSize().getHeight()));

				g.setStroke(new BasicStroke(2f));
				g.setColor(Color.DARK_GRAY);

				int offsetX = (int) origin.getX() % TileConstants.TILE_SIZE;
				int offsetY = (int) origin.getY() % TileConstants.TILE_SIZE;

				for (int i = offsetX; i < getSize().getWidth(); i += TileConstants.TILE_SIZE)
				{
					for (int j = offsetY; j < getSize().getHeight(); j += TileConstants.TILE_SIZE)
					{
						if (i != origin.getX() && j != origin.getY())
							g.fill(new Rectangle(i - 1, j - 1, 2, 2));
					}
				}
			}
		}

		private boolean displayGrid = true;
	}

	private Minimap minimap;
	private Point origin;
	public int MAX_STATE_LAYERS = 102;
	private int MAX_TILE_LAYERS = 100;
	private Integer MINIMAP_LAYER = 150;
	private Integer CURRENT_TOOL_LAYER = 151;
	private Integer COLLISION_LAYER = 101;
	private Integer OBJECT_LAYER = 102;
	private Integer BACKGROUND_LAYER = new Integer(Integer.MIN_VALUE);
	private MainModel model;
	private Handler handler;
}
